using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Storage;
using System.IO;
using System.Xml.Serialization;

namespace BrainWare
{
    class NumberKey
    {
        public int number;
        public GamePadKey key;
        public bool missing;

        public NumberKey(int Number, GamePadKey Key, bool Missing)
        {
            number = Number;
            key = Key;
            missing = Missing;
        }

    }

    class MissingNumberGame
    {
        private Vector2 AButtonPos = new Vector2(300f, 460f);
        private Vector2 BButtonPos = new Vector2(500f, 260f);
        private Vector2 XButtonPos = new Vector2(100f, 260f);
        private Vector2 YButtonPos = new Vector2(300f, 20f);
        private Vector2 ButtonOffset = new Vector2(90f, 50f);
        private Vector2 ShadowOffset = new Vector2(95f, 55f);
        

        Texture2D AButtonTexture;
        Texture2D BButtonTexture;
        Texture2D XButtonTexture;
        Texture2D YButtonTexture;
        Texture2D ButtonShadowTexture;
        Texture2D GameOverTexture;

        private List<int> numbersAvailable;
        private List<NumberKey> numberKeysUsed;
        private List<GamePadKey> gamePadKeysAvailable; 
        private double TimeBetweenNumbers = 2;
        private double timeSinceLastNumber = 0;
        private double startTime = 0; //Time started in seconds
        private double timeRemaining = 0; //Time started in seconds
        private int MaxNumber = 8;
        private int missingNumber;
        private int howManyCorrectInARow;
        private NumberKey keyPressed;

        private bool HighScoreSaved;


        private const int GameLength = 150; // Length of a game in seconds


        private Random rand;
        
        private int currentNumber = -1;

        public MissingNumberGame()
        {
            AButtonTexture = BrainWareGame.Content.Load<Texture2D>("Textures/AButton");
            BButtonTexture = BrainWareGame.Content.Load<Texture2D>("Textures/BButton");
            XButtonTexture = BrainWareGame.Content.Load<Texture2D>("Textures/XButton");
            YButtonTexture = BrainWareGame.Content.Load<Texture2D>("Textures/YButton");
            ButtonShadowTexture = BrainWareGame.Content.Load<Texture2D>("Textures/ButtonShadow");
            GameOverTexture = BrainWareGame.Content.Load<Texture2D>("Textures/GameOver");            

            numbersAvailable = new List<int>();
            numberKeysUsed = new List<NumberKey>();
            gamePadKeysAvailable = new List<GamePadKey>();
                        
            ResetNumbersandKeys();
        }

        void RestartGame(GameTime gametime)
        {
            startTime = gametime.TotalGameTime.TotalSeconds;
            TimeBetweenNumbers = 2;
            timeSinceLastNumber = 0;
            HighScoreSaved = false;

            ResetNumbersandKeys();            
        }
        void ResetNumbersandKeys()
        {
            numbersAvailable.Clear();
            numberKeysUsed.Clear();
            gamePadKeysAvailable.Clear();

            for (int i = 1; i <= MaxNumber ; i++)
            {
                numbersAvailable.Add(i);
            }
            timeSinceLastNumber = 0.5;

            timeRemaining = 60;
            gamePadKeysAvailable.Add(GamePadKey.A);
            gamePadKeysAvailable.Add(GamePadKey.B);
            gamePadKeysAvailable.Add(GamePadKey.X);
            gamePadKeysAvailable.Add(GamePadKey.Y);
        }
 
        public void Draw(RelativeSpriteBatch spriteBatch)
        {
            // If game over draw game over screen
            if (timeRemaining <= 0)
            {
                spriteBatch.Draw(GameOverTexture, new Vector2(0, 0), Color.White);
                Font.Draw(spriteBatch, FontStyle.Score, 650, 545, BrainWareGame.Score);

                if (BrainWareGame.HighScores != null)
                {                    
                    //Draw the high scores
                    for (int i = 0; i < 5; i++)
                    {
                        if (BrainWareGame.HighScores[i] != 0)
                        {
                            Font.Draw(spriteBatch, FontStyle.Large, 955, 223 + i * 48,
                                      BrainWareGame.HighScores[i]);
                        }
                    }
                }

                return;
            }

            // Draw the current score
            Font.Draw(spriteBatch, FontStyle.Large, 1180, 70, BrainWareGame.Score);

            //Draw Time Remaining
            Font.Draw(spriteBatch, FontStyle.Large, 1180, 10, (int)timeRemaining);

            if (keyPressed != null)
            {
                DrawNumberKey(spriteBatch, keyPressed);
            }

            if (numberKeysUsed.Count <= 0) return;
            // Missing number not set so must be showing the numbers to be remembered
            if (numberKeysUsed.Count< 4)
            {
                // Draw the first current numbers
                Font.Draw(spriteBatch, FontStyle.Big, 380, 270, currentNumber);
            }
            else
            {
                for (int i = 0; i < 4; i++)
                {
                    NumberKey numberKey = numberKeysUsed[i];

                   DrawNumberKey(spriteBatch, numberKey);
                }

            }

        }

        private void DrawNumberKey(RelativeSpriteBatch spriteBatch, NumberKey numberKey)
        {
            switch (numberKey.key)
            {
                case GamePadKey.A:
                    spriteBatch.Draw(ButtonShadowTexture, Vector2.Add(AButtonPos, ShadowOffset), Color.White);
                    spriteBatch.Draw(AButtonTexture, Vector2.Add(AButtonPos, ButtonOffset), Color.White);
                    Font.Draw(spriteBatch, FontStyle.Big, AButtonPos, numberKey.number, Color.White);
                    break;
                case GamePadKey.B:
                    spriteBatch.Draw(ButtonShadowTexture, Vector2.Add(BButtonPos, ShadowOffset), Color.White);
                    spriteBatch.Draw(BButtonTexture, Vector2.Add(BButtonPos, ButtonOffset), Color.White);
                    Font.Draw(spriteBatch, FontStyle.Big, BButtonPos, numberKey.number, Color.White);
                    break;
                case GamePadKey.X:
                    spriteBatch.Draw(ButtonShadowTexture, Vector2.Add(XButtonPos, ShadowOffset), Color.White);
                    spriteBatch.Draw(XButtonTexture, Vector2.Add(XButtonPos, ButtonOffset), Color.White);
                    Font.Draw(spriteBatch, FontStyle.Big, XButtonPos, numberKey.number, Color.White);
                    break;
                case GamePadKey.Y:
                    spriteBatch.Draw(ButtonShadowTexture, Vector2.Add(YButtonPos, ShadowOffset), Color.White);
                    spriteBatch.Draw(YButtonTexture, Vector2.Add(YButtonPos, ButtonOffset), Color.White);
                    Font.Draw(spriteBatch, FontStyle.Big, YButtonPos, numberKey.number, Color.White);
                    break;


            }
        }

        public void Update(GameTime gametime)
        {                
            if (rand == null)
            {
                //Gets called once to initialise game using current time
                rand = new Random((int)gametime.TotalRealTime.Ticks);                
                RestartGame(gametime);
            }

            
            if (timeRemaining <= 0)
            {
                // If game over
                //...
                numberKeysUsed.Clear();
                
                if (! HighScoreSaved)
                {
                    HighScoreSaved = true;
                    // Display game over
                    SaveHighScores();
                }
                
                // If user press a start over
                if (InputHelper.GamePads[PlayerIndex.One].APressed)
                {
                    RestartGame(gametime);
                }

                return;

            }

            timeRemaining = GameLength - (gametime.TotalGameTime.TotalSeconds - startTime);

            timeSinceLastNumber += gametime.ElapsedGameTime.TotalSeconds;

            // Waiting for answer
            if (numberKeysUsed.Count == 4)
            {
                // Allows the default game to exit on Xbox 360 and Windows
                if (InputHelper.GamePads[PlayerIndex.One].APressed)
                {
                    CheckAnswer(GamePadKey.A);
                    {
                        ResetNumbersandKeys();
                        BrainWareGame.explosion.AddParticles(AButtonPos);
                        return;
                    }
                }

                if (InputHelper.GamePads[PlayerIndex.One].BPressed)
                {
                    CheckAnswer(GamePadKey.B);
                    {
                        ResetNumbersandKeys();
                        BrainWareGame.explosion.AddParticles(BButtonPos);
                        return;
                    }
                }

                if (InputHelper.GamePads[PlayerIndex.One].XPressed)
                {
                    CheckAnswer(GamePadKey.X);
                    {
                        ResetNumbersandKeys();
                        BrainWareGame.explosion.AddParticles(XButtonPos);
                        return;
                    }
                }
                if (InputHelper.GamePads[PlayerIndex.One].YPressed)
                {
                    CheckAnswer(GamePadKey.Y);
                    {
                        ResetNumbersandKeys();
                        BrainWareGame.explosion.AddParticles(YButtonPos);
                        return;
                    }
                }

            }
            else
            {
                // Is it time to get the next number?
                if (timeSinceLastNumber >= TimeBetweenNumbers)
                {
                    timeSinceLastNumber = 0;

                    keyPressed = null;

                    // If we have shown the user the first 3 random numbers we need to get the new missing number
                    if (numberKeysUsed.Count >= 3)
                    {
                        missingNumber = GetNextNumber();
                        numberKeysUsed.Add(new NumberKey(missingNumber, GetNextGamePadKey(), true));

                        BrainWareGame.explosion.AddParticles(AButtonPos);
                        BrainWareGame.explosion.AddParticles(BButtonPos);
                        BrainWareGame.explosion.AddParticles(XButtonPos);
                        BrainWareGame.explosion.AddParticles(YButtonPos);   
                    }
                    else
                    {
                        // Get the next number to read for the player to remember
                        currentNumber = GetNextNumber();

                        numberKeysUsed.Add(new NumberKey(currentNumber, GetNextGamePadKey(), false));
                        Console.WriteLine(currentNumber.ToString());
                        Sound.Play(currentNumber.ToString());

                        BrainWareGame.explosion.AddParticles(new Vector2(350, 350));
                    }
                }
            }
        }
        
        /// <summary>
        /// Checks the answer was correct and updates the score and starts the next round
        /// </summary>
        /// <param name="gamePadKey"></param>
        private void CheckAnswer(GamePadKey gamePadKey)
        {
            foreach(NumberKey numberKey in numberKeysUsed)
            {
                if (numberKey.key == gamePadKey)
                {
                    keyPressed = numberKey;
                    if (numberKey.missing)
                    {
                        howManyCorrectInARow++;
                        BrainWareGame.Score+= howManyCorrectInARow;
                        Sound.Play(SoundEntry.WellDone);
                        // Decreases the delay between each number call
                        MaxNumber = Math.Min(20, MaxNumber + 1);
                        
                        TimeBetweenNumbers *= 0.9;
                        timeSinceLastNumber = TimeBetweenNumbers - 1;
                    }
                    else
                    {
                        howManyCorrectInARow = 0;
                        BrainWareGame.Score--;
                        BrainWareGame.Score = Math.Max(0,BrainWareGame.Score-1);
                        Sound.Play(SoundEntry.OhNo);
                        // Increases the delay between each number call

                        MaxNumber = Math.Max(4,MaxNumber-1);
                        
                        TimeBetweenNumbers *= 1.1; 
                        // This causes there to be a longer time delay before the next 
                        // round in order to play the giggle and punish user for getting it wrong!.                        
                        timeSinceLastNumber = TimeBetweenNumbers - 2;
                    }
                }
            }
            ResetNumbersandKeys();
        }

        private void NextStage()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        /// <summary>
        /// Returns the next random unused number
        /// </summary>
        /// <returns></returns>
        private int GetNextNumber()
        {            
            int number =0;
            if (numbersAvailable.Count > 0)
            {
                int numberPos = rand.Next(numbersAvailable.Count);
                number = numbersAvailable[numberPos];                
                numbersAvailable.Remove(number);
            }
            return number;
        }

        /// <summary>
        /// Gets the number and next gamepad key pair.
        /// Randomly selects an unused number and unused gamepad key to pair together
        /// </summary>
        /// <returns></returns>
        private GamePadKey GetNextGamePadKey()
        {            
            GamePadKey gameKey = GamePadKey.Down;
            if (numbersAvailable.Count > 0)
            {
                int numberPos = rand.Next(gamePadKeysAvailable.Count);
                gameKey = gamePadKeysAvailable[numberPos];
                gamePadKeysAvailable.Remove(gameKey);
            }
            return gameKey;
        }

        private void PlayNextNumber()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        /// <summary>
        /// Save the high scores to the drive.
        /// </summary>
        private static void SaveHighScores()
        {
            //Insert the score into the high score table
            for (int i = 0; i < 5; i++)
            {
                if (BrainWareGame.Score > BrainWareGame.HighScores[i])
                {
                    //Insert new score
                    BrainWareGame.HighScores.Insert(i, BrainWareGame.Score);
                    //And remove the lowest from the bottom
                    BrainWareGame.HighScores.RemoveAt(5);
                    break;
                }
            }
            
            /* Couldn't get to work on 360
            if ((BrainWareGame.StorageDevice != null) &&
                BrainWareGame.StorageDevice.IsConnected)
            {
                SaveHighScoresCallback(null);
            }
            else
            {
                Guide.BeginShowStorageDeviceSelector(
                    new AsyncCallback(SaveHighScoresCallback), null);
            }*/
        }

        /// <summary>
        /// Callback method for saving the high scores to the drive.
        /// </summary>
        private static void SaveHighScoresCallback(IAsyncResult result)
        {
            if ((result != null) && result.IsCompleted)
            {
                BrainWareGame.StorageDevice = Guide.EndShowStorageDeviceSelector(result);
            }
            if ((BrainWareGame.StorageDevice != null) &&
                BrainWareGame.StorageDevice.IsConnected)
            {
                using (StorageContainer storageContainer =
                    BrainWareGame.StorageDevice.OpenContainer("BrainWare"))
                {
                    string highscoresPath = Path.Combine(storageContainer.Path,
                                                         "highscores.xml");
                    using (FileStream file = File.Create(highscoresPath))
                    {
                        XmlSerializer serializer = new XmlSerializer(typeof(List<int>));
                        serializer.Serialize(file, BrainWareGame.HighScores);
                    }
                }
            }
        }

    }
}
